package com.commonlib.downloadmgr.base;

import java.io.Serializable;

import android.content.Context;
import android.view.View;

import com.commonlib.downloadmgr.base.DatabaseBuilder.Persistable;

/**
 * Single remote file download task
 * 
 */
public abstract class DownloadBaseJob<T> implements Serializable, IDownloadControl, Comparable<DownloadBaseJob<T>>{
	private static final String TAG = DownloadBaseJob.class.getSimpleName();
	private static final long serialVersionUID = 6956068830691069757L;
	
	
	public interface ErrorCode{
		public static final int ERROR_IO = 100;
		public static final int ERROR_NETWORK = 200;
		public static final int ERROR_OTHER = 300;
		public static final int ERROR_OTHER_DOWNLOADING = ERROR_OTHER + 1;
		public static final int ERROR_SERVER = 400;
	};
	
	public static final int STATE_IDLE = 0x01;
	public static final int STATE_DOWNLOADING = 0x02;
	public static final int STATE_PAUSED = 0x03;
	public static final int STATE_SUCCESS = 0x04;
	public static final int STATE_FAILED = 0x05;
	
	protected Context context;
	protected DownloadThread<T> mThread;
	protected DownloadBaseHelper<T> mHelper;
	protected IDownloadStateListener<T> stateListener;
	protected T entity;
	@Persistable
	public long job_initTimeMs;
	@Persistable
	public String job_id;
	@Persistable
	public String job_url;
	protected int mProgress;
	@Persistable
	public long job_mTotalSize;
	@Persistable
	public long job_mDownloadedSize;
	@Persistable
	public int job_state = STATE_IDLE;
	protected int errorCode;
	protected View container;
	protected boolean showNotification;
	
	protected DownloadBaseJob(){
		
	}
	
	public DownloadBaseJob(Context context, DownloadBaseHelper<T> mHelper, T entity, String id, String url) {
		this.context = context;
		this.mHelper = mHelper;
		this.entity = entity;
		this.job_id = id;
		this.job_url = url;
		this.job_initTimeMs = System.currentTimeMillis();
	}
	
	public void resetState(){
		setState(STATE_IDLE, true);
		job_mTotalSize = 0;
		job_mDownloadedSize = 0;
		mProgress = 0;
	}
	
	public Context getContext() {
		return context;
	}

	public void setContext(Context context) {
		this.context = context;
	}

	public DownloadBaseHelper<T> getmHelper() {
		return mHelper;
	}

	public void setmHelper(DownloadBaseHelper<T> mHelper) {
		this.mHelper = mHelper;
	}
	
	public IDownloadStateListener<T> getStateListener() {
		return stateListener;
	}

	public void setStateListener(IDownloadStateListener<T> stateListener) {
		this.stateListener = stateListener;
	}

	public T getEntity() {
		return entity;
	}

	public void setEntity(T entity) {
		this.entity = entity;
	}

	public long getInitTimeMs() {
		return job_initTimeMs;
	}

	public void setInitTimeMs(long curTimeMs) {
		this.job_initTimeMs = curTimeMs;
	}

	public String getId() {
		return job_id;
	}

	public void setId(String id) {
		this.job_id = id;
	}
	
	public String getUrl() {
		return job_url;
	}

	public void setUrl(String url) {
		this.job_url = url;
	}

	public int getmProgress() {
		return mProgress;
	}

	public void setmProgress(int mProgress) {
		this.mProgress = mProgress;
	}

	public long getmTotalSize() {
		return job_mTotalSize;
	}

	public void setmTotalSize(long mTotalSize) {
		this.job_mTotalSize = mTotalSize;
	}

	public long getmDownloadedSize() {
		return job_mDownloadedSize;
	}

	public void setmDownloadedSize(long mDownloadedSize) {
		this.job_mDownloadedSize = mDownloadedSize;
		if (job_mTotalSize == 0) {
			mProgress = 0;
		}else{
			mProgress = (int) (100*this.job_mDownloadedSize/job_mTotalSize);
		}
		if (mProgress > 100) {
			mProgress = 100;
		}
		postUiUpdate(false);
	}

	public int getState() {
		return job_state;
	}

	public void setState(int state, boolean updateCache) {
		Log.v(TAG, "setState, new state:" + state + ", old state:" + this.job_state);
		boolean isNotify = state!=this.job_state ? true : false;
		this.job_state = state;
		if (isNotify) {
			showNotification();
			notifyStateChanged();
			postUiUpdate(true);
		}
		if (updateCache) {
			getDownloadManager().updateData(this);
		}
	}
	
	public int getErrorCode() {
		return errorCode;
	}

	public void setErrorCode(int errorCode) {
		this.errorCode = errorCode;
	}

	public View getContainer() {
		return container;
	}

	public void setContainer(View container) {
		this.container = container;
	}

	
	public boolean isShowNotification() {
		return showNotification;
	}

	public void setShowNotification(boolean showNotification) {
		this.showNotification = showNotification;
	}

	/**
	 * Deprecated invoking outside
	 */
	@Deprecated
	public void onStart() {
		Log.v(TAG, "onStart");
		if (mThread == null || !mThread.isAlive()) {
			resetState();
			if (mThread != null){
				mThread.interrupt();
				mThread = null;
				Log.v(TAG, "mThread is not null but unAlive");
			}
			mThread = new DownloadThread<T>(context, this);
			mThread.start();
			Log.v(TAG, "create a new Thread and start it, thread id:" + mThread.getId());
		}else{
			synchronized (this) {
				Log.v(TAG, "mThread is not null and alive, notify it");
				setState(STATE_DOWNLOADING, true);
				this.notifyAll();
				mThread.interrupt();
			}
		}
	}
	
	@Override
	public void onRetry(){
		if (job_state == STATE_FAILED) {
			Log.v(TAG, "onRetry success");
			getDownloadManager().addDownloadJob(this);
		}else{
			Log.e(TAG, "onRetry failed, wrong state " + job_state);
		}
	}
	
	@SuppressWarnings("deprecation")
	@Override
	public void onResume() {
		if (job_state == STATE_PAUSED) {
			Log.v(TAG, "onResume success, calling onStart to execute mThread");
			onStart();
		}else{
			Log.e(TAG, "onResume failed, wrong state " + job_state);
		}
	}

	@Override
	public void onPause() {
		if (job_state == STATE_DOWNLOADING) {
			Log.v(TAG, "onPause success");
			setState(STATE_PAUSED, true);
		}else{
			Log.e(TAG, "onPause failed, wrong state " + job_state);
		}
	}

	@Override
	public void onDelete() {
		Log.v(TAG, "onDelete");
		synchronized (this) {
			job_state = STATE_FAILED;
			this.notifyAll();
		};
		getDownloadManager().removeDownloadJob(job_id, false,  true);
		getDownloadManager().executeWaitingJob();
		getDownloadManager().deleteData(job_id);
		mHelper.deleteDownloadedFile(context, this);
		mHelper.cancelNotifcation(this);
	}
	
	/**
	 * callback when current job is finished
	 */
	public void onFinish(){
		Log.v(TAG, "onFinish");
		synchronized (this) {
			this.notifyAll();
			mThread.interrupt();
		};
		getDownloadManager().onFilishJob(job_id);
	}
	
	/**
	 * Invoking callback method when the state is changed
	 */
	private void notifyStateChanged(){
		if (stateListener != null) {
			stateListener.onStateChanged(this);
		}
	}
	
	/**
	 * Show notification when download successfully
	 */
	private void showNotification(){
		if (showNotification && job_state == STATE_SUCCESS) {
			mHelper.showNotifcation(this);
		}
	}

	@Override
	public int compareTo(DownloadBaseJob<T> another) {
		long result = this.job_initTimeMs - another.getInitTimeMs();
      return result > 0 ? 1 : ( result == 0 ? 0 : -1 );
	}
	
	/**
	 * get download manager
	 * @return
	 */
	public abstract DownloadBaseManager<T> getDownloadManager();
	
	/**
	 * update view about job state
	 */
	public abstract void postUiUpdate(boolean forceUpdate);
		
}
